package fatworm.query.expression;

import fatworm.query.Plan;
import fatworm.query.Scan;
import fatworm.query.constant.Constant;
import fatworm.query.constant.FloatConstant;
import fatworm.query.constant.IntConstant;
import fatworm.storage.table.*;
import fatworm.util.*;

public class OpExpression implements Expression {
	
	private Expression lexp, rexp;
	private int opcode;
	
	public OpExpression(int opcode, Expression lexp, Expression rexp) {
		this.opcode = opcode;
		this.lexp = lexp;
		this.rexp = rexp;
	}
	
	public boolean isConstant() {
		return false;
	}
	
	public boolean isFieldName() {
		return false;
	}
	
	public boolean isOp() {
		return true;
	}
	
	public boolean isSubquery() {
		return false;
	}
	
	public Constant asConstant() {
		Lib.assertNotReached();
		return null;
	}
	
	public String asFieldName() {
		Lib.assertNotReached();
		return null;
	}
	
	public Plan asSubquery() {
		Lib.assertNotReached();
		return null;
	}
	
	public Constant evaluate(Scan s) {
		
		Constant lvalue = lexp.evaluate(s);
		Constant rvalue = rexp.evaluate(s);
		
		if (rvalue instanceof IntConstant && lvalue instanceof IntConstant && opcode != Const.DIV) {
			
			int l = ((Integer)((IntConstant)lvalue).asJavaVal()).intValue();
			int r = ((Integer)((IntConstant)rvalue).asJavaVal()).intValue();
			int ans = 0;
			
			switch (opcode) {
			case Const.PLUS: 
				ans = l + r;
				break;
			case Const.MINUS:
				ans = l - r;
				break;
			case Const.MULT:
				ans = l * r;
				break;
			case Const.MOD:
				ans = l % r;
				break;
			default:
				Lib.assertNotReached();
			}
			
			return new IntConstant(ans);
		}
		
		float l = 0, r = 0, ans = 0;
		
		if (lvalue instanceof IntConstant)
			l = ((Integer)((IntConstant)lvalue).asJavaVal()).floatValue();
		else if (lvalue instanceof FloatConstant)
			l = ((Float)((FloatConstant)lvalue).asJavaVal()).floatValue();
		else 
			Lib.assertNotReached();
		
		if (rvalue instanceof IntConstant)
			r = ((Integer)((IntConstant)rvalue).asJavaVal()).floatValue();
		else if (lvalue instanceof FloatConstant)
			r = ((Float)((FloatConstant)rvalue).asJavaVal()).floatValue();
		else 
			Lib.assertNotReached();
		
		switch (opcode) {
		case Const.PLUS: 
			ans = l + r;
			break;
		case Const.MINUS:
			ans = l - r;
			break;
		case Const.MULT:
			ans = l * r;
			break;
		case Const.DIV:
			ans = l / r;
			break;
		case Const.MOD:
			ans = Lib.floatMod(l, r);
			break;
		default:
			Lib.assertNotReached();
		}
		
		return new FloatConstant(ans);
		
	}
	
	public boolean isIn(Schema sch) {
		return lexp.isIn(sch) && rexp.isIn(sch);
	}
	
	public String toString() {
		String op = "";
		
		switch (opcode) {
		case Const.PLUS: 
			op = "+";
			break;
		case Const.MINUS:
			op = "-";
			break;
		case Const.MULT:
			op = "*";
			break;
		case Const.DIV:
			op = "/";
			break;
		case Const.MOD:
			op = "%";
			break;
		default:
			Lib.assertNotReached();
		}
		
		return lexp.toString() + op + rexp.toString();
	}
	
}